import proxyquire from 'proxyquire'
import * as chai from 'chai'
import * as sinon from 'sinon'
import {checkContainerConfig} from '../src/util'

const expect = chai.expect

describe('copyToTargetBin', () => {
    let fsStub: any;
    let stepStub: any;
    let copyToTargetBinProxy: any;

    beforeEach(() => {
        process.env["PIPELINE_LOCALE"] = 'zh_CN';

        fsStub = {
            existsSync: sinon.stub(),
            unlinkSync: sinon.stub(),
            copyFileSync: sinon.stub(),
            statSync: sinon.stub(),
            chmodSync: sinon.stub()
        };

        stepStub = {
            debug: sinon.stub()
        };

        const utilModule = proxyquire('../src/util', {
            fs: fsStub,
            '@flow-step/step-toolkit': stepStub
        });

        copyToTargetBinProxy = utilModule.copyToTargetBin;
    });

    afterEach(() => {
        delete process.env["PIPELINE_LOCALE"];
        sinon.restore();
    });

    it('should copy file and set permissions to 755 if not already set', () => {
        const sourceBinFile = '/path/to/source.bin';
        const targetBinFile = '/path/to/target.bin';

        fsStub.existsSync.returns(true);
        fsStub.statSync.returns({mode: 0o644});

        const result = copyToTargetBinProxy(sourceBinFile, targetBinFile);
        expect(result).to.equal(targetBinFile);
        expect(fsStub.existsSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.unlinkSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.copyFileSync.calledWith(sourceBinFile, targetBinFile)).to.be.true;
        expect(fsStub.statSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.chmodSync.calledWith(targetBinFile, 0o755)).to.be.true;
    });

    it('should not change permissions if they are already 755', () => {
        const sourceBinFile = '/path/to/source.bin';
        const targetBinFile = '/path/to/target.bin';

        fsStub.existsSync.returns(false);
        fsStub.statSync.returns({mode: 0o755});

        const result = copyToTargetBinProxy(sourceBinFile, targetBinFile);
        expect(result).to.equal(targetBinFile);
        expect(fsStub.existsSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.unlinkSync.called).to.be.false;
        expect(fsStub.copyFileSync.calledWith(sourceBinFile, targetBinFile)).to.be.true;
        expect(fsStub.statSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.chmodSync.called).to.be.false;
    });

    it('should throw an error if setting permissions fails', () => {
        const sourceBinFile = '/path/to/source.bin';
        const targetBinFile = '/path/to/target.bin';

        fsStub.existsSync.returns(false);
        fsStub.statSync.returns({mode: 0o644});
        fsStub.chmodSync.throws(new Error('Permission denied'));


        expect(() => copyToTargetBinProxy(sourceBinFile, targetBinFile)).to.throw('Error setting file permissions: Permission denied');
    });
});


describe('prepareKubectlArgs', () => {
    let fsMock: any;
    let prepareKubectlArgsProxy: any;

    beforeEach(() => {
        fsMock = {
            mkdirSync: sinon.stub(),
            writeFileSync: sinon.stub()
        };

        prepareKubectlArgsProxy = proxyquire('../src/util', {
            fs: fsMock,
        }).prepareKubectlArgs;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should prepare kubectl args for custom cluster type', () => {
        const params = {
            clusterType: 'custom',
            namespace: 'test-namespace',
            insecureSkipTlsVerify: true
        };

        const kubernetesCertificate = {
            kubeConfig: 'fake-kube-config'
        };

        const result = prepareKubectlArgsProxy(params, kubernetesCertificate, '.kube_1234567890');

        expect(fsMock.mkdirSync.calledOnceWithExactly(sinon.match.string, { recursive: true })).to.be.true;
        expect(fsMock.writeFileSync.calledOnceWithExactly(sinon.match.string, 'fake-kube-config')).to.be.true;
        expect(result).to.deep.equal(['--kubeconfig=.kube_1234567890/config', '--insecure-skip-tls-verify=true', '--namespace=test-namespace']);
    });

    it('should prepare kubectl args for non-custom cluster type', () => {
        const params = {
            clusterType: 'non-custom',
            namespace: 'test-namespace',
            insecureSkipTlsVerify: false
        };

        const kubernetesCertificate = {
            miranaProxy: 'https://mirana-proxy.com',
            jwtToken: 'fake-jwt-token'
        };

        const result = prepareKubectlArgsProxy(params, kubernetesCertificate,'.kube_1234567890');

        expect(fsMock.mkdirSync.calledOnceWithExactly(sinon.match.string, { recursive: true })).to.be.true;
        expect(fsMock.writeFileSync.calledOnceWithExactly(sinon.match.string, '')).to.be.true;
        expect(result).to.deep.equal(['--kubeconfig=.kube_1234567890/config', '--server=https://mirana-proxy.com', '--token=fake-jwt-token', '--insecure-skip-tls-verify=true', '--namespace=test-namespace']);
    });

    it('should throw an error if kubeConfig is undefined or empty for custom cluster type', () => {
        const params = {
            clusterType: 'custom',
            namespace: 'test-namespace',
            insecureSkipTlsVerify: true
        };

        const kubernetesCertificate = {
            kubeConfig: ''
        };

        expect(() => prepareKubectlArgsProxy(params, kubernetesCertificate)).to.throw('未获取到合法的Kubernetes证书文件，请检查流水线配置后再试');
    });
});

describe('checkContainerConfig', () => {
    let stepMock: any;
    let checkContainerConfigProxy: any;

    beforeEach(() => {
        stepMock = {
            infoV1: sinon.stub(),
            debug: sinon.stub()
        };

        checkContainerConfigProxy = proxyquire('../src/util', {
            '@flow-step/step-toolkit': stepMock
        }).checkContainerConfig;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should log container count and set containerName when there is only one container', () => {
        const params = {
            kind: 'deployments',
            name: 'test-app',
            containerName: 'dummy-name'
        };
        const output = JSON.stringify({
            spec: {
                template: {
                    spec: {
                        containers: [
                            { name: 'real-container-name' }
                        ]
                    }
                }
            }
        });

        checkContainerConfigProxy(params, output);

        expect(stepMock.infoV1.secondCall.args[0]).to.equal('当前应用test-app中包含1个容器定义');
        expect(stepMock.debug.firstCall.args[0]).to.equal('set containerName value: real-container-name');
        expect(params.containerName).to.equal('real-container-name');
    });

    it('should log container count without changing containerName when there are multiple containers', () => {
        const params = {
            kind: 'statefulsets',
            name: 'test-app',
            containerName: 'dummy-name'
        };
        const output = JSON.stringify({
            spec: {
                template: {
                    spec: {
                        containers: [
                            { name: 'container1' },
                            { name: 'container2' }
                        ]
                    }
                }
            }
        });

        checkContainerConfigProxy(params, output);

        expect(stepMock.infoV1.secondCall.args[0]).to.equal('当前应用test-app中包含2个容器定义');
        expect(params.containerName).to.equal('dummy-name');
    });

    it('should not perform any actions for unsupported kinds', () => {
        const params = {
            kind: 'CronJob',
            name: 'test-app',
            containerName: 'dummy-name'
        };
        const output = JSON.stringify({});

        checkContainerConfigProxy(params, output);
        expect(params.containerName).to.equal('dummy-name');
    });
});